<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="SQ1cU" id="SQ1cU"><span data-lake-id="ua4fbe6a6" id="ua4fbe6a6">典型回答</span></h1>
  <p data-lake-id="u715fa500" id="u715fa500"><br></p>
  <p data-lake-id="ub864a3f9" id="ub864a3f9"><span data-lake-id="ud53cd3ff" id="ud53cd3ff">在Spring Boot中，通过SpringApplication类的静态方法run来启动Web项目。当我们在main方法中调用run方法时，Spring Boot使用一个</span><strong><span data-lake-id="u485b184e" id="u485b184e">内嵌的Tomcat服务器</span></strong><span data-lake-id="ub3e7a6f7" id="ub3e7a6f7">，并将其配置为处理Web请求。</span></p>
  <p data-lake-id="u963e6589" id="u963e6589"><span data-lake-id="ue8af8913" id="ue8af8913">​</span><br></p>
  <p data-lake-id="ua00de2d7" id="ua00de2d7"><span data-lake-id="ufe51eab6" id="ufe51eab6">当应用程序启动时，Spring Boot会自动扫描应用程序中所有的Spring组件，并使用默认的配置来启动内嵌的Tomcat服务器。在默认情况下，Spring Boot会自动配置大部分的Web开发所需的配置，包括请求处理、视图解析、静态资源处理等。</span></p>
  <p data-lake-id="uf8161310" id="uf8161310"><span data-lake-id="u2750f854" id="u2750f854">​</span><br></p>
  <p data-lake-id="uae20c31a" id="uae20c31a"><span data-lake-id="u73d42196" id="u73d42196">这样，在应用程序启动后，我们就可以通过Web浏览器访问应用程序了。例如，在默认情况下，可以通过访问http://localhost:8080 来访问应用程序的首页。</span></p>
  <p data-lake-id="u497818b0" id="u497818b0"><span data-lake-id="u77027df3" id="u77027df3">​</span><br></p>
  <p data-lake-id="u4305c58d" id="u4305c58d"><span data-lake-id="uadcb516f" id="uadcb516f">但是，很多人都会忽略一个关键的步骤（网上很多介绍SpringBoot启动流程的都没提到），那就是Web容器的启动，及Tomcat的启动其实也是在这个步骤。</span></p>
  <p data-lake-id="u43998b2b" id="u43998b2b"><span data-lake-id="ub9f3548b" id="ub9f3548b">​</span><br></p>
  <h3 data-lake-id="CeKdl" id="CeKdl"><span data-lake-id="u41f3c116" id="u41f3c116">实现原理</span></h3>
  <p data-lake-id="u8e243f98" id="u8e243f98"><span data-lake-id="u2441714f" id="u2441714f">​</span><br></p>
  <p data-lake-id="u99ce6ee0" id="u99ce6ee0"><span data-lake-id="ud0add47b" id="ud0add47b">在SpringBoot的启动流程中，会调用SpringApplication.run方法，这个方法会有一个步骤进行啥下文刷线（refreshContext），然后这个过程中，会调用一个关键的方法onRefresh。</span></p>
  <p data-lake-id="u9d8e2aa8" id="u9d8e2aa8"><span data-lake-id="ube78c32c" id="ube78c32c">​</span><br></p>
  <p data-lake-id="u16a33ddf" id="u16a33ddf"><br></p>
  <p data-lake-id="u66231fd1" id="u66231fd1"><span data-lake-id="uea397e84" id="uea397e84">调用链：</span><code data-lake-id="u93529978" id="u93529978"><span data-lake-id="u00dd181a" id="u00dd181a">SpringApplication.run -&gt; refreshContext -&gt; refresh -&gt; onRefresh</span></code></p>
  <p data-lake-id="ude78c50c" id="ude78c50c"><br></p>
  <p data-lake-id="u0ddb647e" id="u0ddb647e"><span data-lake-id="ufcc39dfa" id="ufcc39dfa">在refresh-&gt; onRefresh中，这里会调用到ServletWebServerApplicationContext的onRefresh中：</span></p>
  <p data-lake-id="uf5126ba3" id="uf5126ba3"><span data-lake-id="u926a4920" id="u926a4920">​</span><br></p>
  <pre lang="java"><code>
@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}


private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null &amp;&amp; servletContext == null) {
        StartupStep createWebServer = getApplicationStartup().start("spring.boot.webserver.create");
        ServletWebServerFactory factory = getWebServerFactory();
        createWebServer.tag("factory", factory.getClass().toString());
        this.webServer = factory.getWebServer(getSelfInitializer());
        createWebServer.end();
        getBeanFactory().registerSingleton("webServerGracefulShutdown",
                new WebServerGracefulShutdownLifecycle(this.webServer));
        getBeanFactory().registerSingleton("webServerStartStop",
                new WebServerStartStopLifecycle(this, this.webServer));
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}
</code></pre>
  <p data-lake-id="u4ab25a6c" id="u4ab25a6c"><span data-lake-id="ua1c73458" id="ua1c73458">​</span><br></p>
  <p data-lake-id="ubc16bfab" id="ubc16bfab"><span data-lake-id="ue4788984" id="ue4788984">这里面的&#x0;createWebServer方法中，调用到factory.getWebServer(getSelfInitializer());的时候，factory有三种实现，分别是JettyServletWebServerFactory、TomcatServletWebServerFactory、UndertowServletWebServerFactory这三个，默认使用TomcatServletWebServerFactory。</span></p>
  <p data-lake-id="ue97e1225" id="ue97e1225"><span data-lake-id="u5bfacb3f" id="u5bfacb3f">​</span><br></p>
  <p data-lake-id="u6513420a" id="u6513420a"><span data-lake-id="u488dec59" id="u488dec59">TomcatServletWebServerFactory的getWebServer方法如下，这里会创建一个Tomcat</span></p>
  <pre lang="java"><code>
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    Tomcat tomcat = new Tomcat();
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    for (LifecycleListener listener : this.serverLifecycleListeners) {
        tomcat.getServer().addLifecycleListener(listener);
    }
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);
}
</code></pre>
  <p data-lake-id="u36d53cf5" id="u36d53cf5"><span data-lake-id="uf3828cf3" id="uf3828cf3"></span></p>
  <p data-lake-id="u6cf3389e" id="u6cf3389e"><span data-lake-id="ud27efbbe" id="ud27efbbe">&#x0;在最后一步getTomcatWebServer(tomcat);的代码中，会创建一个TomcatServer，并且把他启动：</span></p>
  <p data-lake-id="ucea3ad8a" id="ucea3ad8a"><span data-lake-id="u70aface5" id="u70aface5">​</span><br></p>
  <pre lang="java"><code>
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    return new TomcatWebServer(tomcat, getPort() &gt;= 0, getShutdown());
}


public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
    initialize();
}
</code></pre>
  <p data-lake-id="ua24444e1" id="ua24444e1"><span data-lake-id="uf8706a7d" id="uf8706a7d"></span></p>
  <p data-lake-id="u53ec8961" id="u53ec8961"><span data-lake-id="u683c8fb9" id="u683c8fb9">&#x0;接下来在initialize中完成了tomcat的启动。</span></p>
  <p data-lake-id="ufbf2af95" id="ufbf2af95"><span data-lake-id="u57e5ced8" id="u57e5ced8">​</span><br></p>
  <p data-lake-id="udf6a75d9" id="udf6a75d9"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1700295160982-af970710-9c20-4fa9-a387-e6f129c28a25.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_29%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u8cab2878" id="u8cab2878"><span data-lake-id="u8091d30c" id="u8091d30c">​</span><br></p>
  <h1 data-lake-id="YByYT" id="YByYT"><span data-lake-id="u48c7f916" id="u48c7f916">扩展知识</span></h1>
  <p data-lake-id="ud07cc67e" id="ud07cc67e"><br></p>
  <h2 data-lake-id="z0GeS" id="z0GeS"><span data-lake-id="ua73de909" id="ua73de909">修改web服务器</span></h2>
  <p data-lake-id="u428eef01" id="u428eef01"><span data-lake-id="u444c9125" id="u444c9125">​</span><br></p>
  <p data-lake-id="u1100fba3" id="u1100fba3"><strong><span data-lake-id="u744b4ba8" id="u744b4ba8">除了默认的Tomcat之外，Spring Boot还支持内嵌Jetty和Undertow服务器。</span></strong></p>
  <p data-lake-id="u57365aa6" id="u57365aa6"><span data-lake-id="ub3ea1b8f" id="ub3ea1b8f">​</span><br></p>
  <p data-lake-id="ub283b385" id="ub283b385"><span data-lake-id="ufeca6a55" id="ufeca6a55">我们可以通过修改pom.xml文件中的依赖项来切换到其他的内嵌Web服务器。例如，如果我们想使用Undertow作为内嵌服务器，我们可以将以下依赖项添加到pom.xml文件中：</span></p>
  <p data-lake-id="udade57b6" id="udade57b6"><span data-lake-id="u6b951453" id="u6b951453">​</span><br></p>
  <pre lang="java"><code>
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-undertow&lt;/artifactId&gt;
&lt;/dependency&gt;
</code></pre>
  <p data-lake-id="ua9f80b8e" id="ua9f80b8e"><br></p>
  <p data-lake-id="ueb85d65f" id="ueb85d65f"><span data-lake-id="u413beebd" id="u413beebd">除了修改依赖项之外，我们还可以通过在application.properties或application.yml文件中设置server属性来配置内嵌Web服务器。例如，如果我们想将端口号从默认的8080更改为9090，我们可以在application.properties文件中添加以下属性：</span></p>
  <p data-lake-id="u6df3dada" id="u6df3dada"><span data-lake-id="u85a4f630" id="u85a4f630">​</span><br></p>
  <pre lang="java"><code>
server.port=9090
</code></pre>
  <p data-lake-id="u8c6a3f13" id="u8c6a3f13"><br></p>
  <p data-lake-id="u4ffa25e0" id="u4ffa25e0"><span data-lake-id="u0c1c10c7" id="u0c1c10c7">除了端口号之外，我们还可以在这里设置其他的Web服务器属性，例如上下文路径、SSL证书等。更多有关配置内嵌Web服务器的信息，可以参考Spring Boot官方文档。</span></p>
  <p data-lake-id="u51930d7a" id="u51930d7a"><br></p>
  <p data-lake-id="uada2bae0" id="uada2bae0"><br></p>
 </body>
</html>